#include "Experiment.h"
#include "PsoParticle.h"

#include <gsl/gsl_statistics.h>
#include <gsl/gsl_sort.h>

#include <QtXml/QDomNode>



namespace jcrada {

    Experiment::Experiment(const std::string& description,
            int iterations, int population_size, int body_parts,
            int record_best_creatures_modulo) :
    _description(description),
    _evolved_creatures(NULL), //Se asigna directo de la evolución.
    _best_creatures(new Creature*[iterations / record_best_creatures_modulo]),
    _best_creature_morphology(NULL),
    _preferred_morphology(NULL),

    _record_best_creatures_modulo(record_best_creatures_modulo),
    _body_parts(body_parts),
    _current_iteration(-1),
    _hidden_layers(-1),
    _iterations(iterations),
    _max_mass(-1),
    _max_size(-1),
    _min_mass(-1),
    _min_size(-1),
    _morphologies(-1),
    _neurons_per_layer(-1),
    _population_size(population_size),
    _time_in_seconds(0),

    _fitness_in_time(new double[iterations]),
    _creature_fitness(new double[population_size]),
    _morphology_index(new int[population_size]),
    _root_shape(new BodyPart::eShape[population_size]),
    _shape_capsules(new int[population_size]),
    _shape_cones(new int[population_size]),
    _shape_cylinders(new int[population_size]),
    _shape_parallelepipeds(new int[population_size]),
    _shape_spheres(new int[population_size]),

    _creature_mass(new double[population_size]),
    _root_mass(new double[population_size]),
    _non_root_mass_mean(new double[population_size]),

    _creature_volume(new double[population_size]),
    _root_volume(new double[population_size]),
    _non_root_volume_mean(new double[population_size]),

    _swing_x_blocked(new int[population_size]),
    _swing_x_medium(new int[population_size]),
    _swing_x_free(new int[population_size]),
    _twist_y_blocked(new int[population_size]),
    _twist_y_medium(new int[population_size]),
    _twist_y_free(new int[population_size]),
    _swing_z_blocked(new int[population_size]),
    _swing_z_medium(new int[population_size]),
    _swing_z_free(new int[population_size]),
    _swing_x(new double*[population_size]),
    _twist_y(new double*[population_size]),
    _swing_z(new double*[population_size]),
    _pso(NULL) {
        for (int i = 0; i < _iterations / _record_best_creatures_modulo; ++i) {
            _best_creatures[i] = NULL;
        }
        for (int i = 0; i < _iterations; ++i) {
            _fitness_in_time[i] = -1;
        }
        for (int i = 0; i < _population_size; ++i) {
            _creature_fitness[i] = 0;
            _morphology_index[i] = -1;
            _root_shape[i] = BodyPart::S_NONE;
            _shape_capsules[i] = 0;
            _shape_cones[i] = 0;
            _shape_cylinders[i] = 0;
            _shape_parallelepipeds[i] = 0;
            _shape_spheres[i] = 0;
            _creature_mass[i] = 0;
            _root_mass[i] = 0;
            _non_root_mass_mean[i] = 0;
            _creature_volume[i] = 0;
            _root_volume[i] = 0;
            _non_root_volume_mean[i] = 0;
            _swing_x_blocked[i] = 0;
            _swing_x_medium[i] = 0;
            _swing_x_free[i] = 0;
            _twist_y_blocked[i] = 0;
            _twist_y_medium[i] = 0;
            _twist_y_free[i] = 0;
            _swing_z_blocked[i] = 0;
            _swing_z_medium[i] = 0;
            _swing_z_free[i] = 0;
        }

        for (int i = 0; i < _population_size; ++i) {
            _swing_x[i] = new double[_body_parts - 1];
            _twist_y[i] = new double[_body_parts - 1];
            _swing_z[i] = new double[_body_parts - 1];
            for (int j = 0; j < _body_parts - 1; ++j) {
                _swing_x[i][j] = 0;
                _twist_y[i][j] = 0;
                _swing_z[i][j] = 0;
            }
        }
    }

    Experiment::~Experiment() {

        for (int i = 0; i < _iterations / _record_best_creatures_modulo; ++i) {
            if (_best_creatures[i]) {
                delete _best_creatures[i];
            }
        }
        delete [] _best_creatures;

        delete [] _fitness_in_time;
        if (_best_creature_morphology) {
            delete _best_creature_morphology;
        }
        if (_preferred_morphology) {
            delete _preferred_morphology;
        }
        delete [] _creature_fitness;
        delete [] _morphology_index;
        delete [] _shape_capsules;
        delete [] _shape_cones;
        delete [] _shape_cylinders;
        delete [] _shape_parallelepipeds;
        delete [] _shape_spheres;
        delete [] _creature_mass;
        delete [] _root_mass;
        delete [] _non_root_mass_mean;
        delete [] _creature_volume;
        delete [] _root_volume;
        delete [] _non_root_volume_mean;
        delete [] _swing_x_blocked;
        delete [] _swing_x_medium;
        delete [] _swing_x_free;
        delete [] _twist_y_blocked;
        delete [] _twist_y_medium;
        delete [] _twist_y_free;
        delete [] _swing_z_blocked;
        delete [] _swing_z_medium;
        delete [] _swing_z_free;
        for (int i = 0; i < _population_size; ++i) {
            delete [] _swing_x[i];
            delete [] _twist_y[i];
            delete [] _swing_z[i];
        }
        delete [] _swing_x;
        delete [] _twist_y;
        delete [] _swing_z;
    }

    void Experiment::analyze() {
        for (int i = 0; i < _population_size; ++i) {
            Creature& creature = *_evolved_creatures[i];
            _creature_fitness[i] = creature.getFitness();
            _creature_mass[i] = 0;
            _creature_volume[i] = 0;
            for (int j = 0; j < creature.getNumberOfBodyParts(); ++j) {
                BodyPart& part = creature.getBodyPart(j);
                _creature_mass[i] += part.getMass();
                _creature_volume[i] += part.getVolume();
                if (part.isRoot()) {
                    //hay solo una raíz :P
                    _root_mass[i] = part.getMass();
                    _root_volume[i] = part.getVolume();
                    _root_shape[i] = part.getShapeWithoutOrientation();
                } else {
                    _non_root_mass_mean[i] += part.getMass();
                    _non_root_volume_mean[i] += part.getVolume();
                }
                countShape(i, part.getShape());
            }
            _non_root_mass_mean[i] /= creature.getNumberOfBodyParts();
            _non_root_volume_mean[i] /= creature.getNumberOfBodyParts();
            for (int j = 0; j < creature.getNumberOfConstraints(); ++j) {
                Constraint& constraint = creature.getConstraint(j);
                countConstraintLimits(i, constraint.getConeSwingX(),
                        constraint.getConeTwistY(), constraint.getConeSwingZ());
            }
            loadConstraintLimits(i, creature);
        }
    }

    void Experiment::countShape(int creature_index, BodyPart::eShape shape) {
        switch (shape) {
            case BodyPart::S_BOX : ++_shape_parallelepipeds[creature_index];
                break;
            case BodyPart::S_CAPSULE_X :
            case BodyPart::S_CAPSULE_Y :
            case BodyPart::S_CAPSULE_Z : ++_shape_capsules[creature_index];
                break;
            case BodyPart::S_CONE_X :
            case BodyPart::S_CONE_Y :
            case BodyPart::S_CONE_Z : ++_shape_cones[creature_index];
                break;
            case BodyPart::S_CYLINDER_X :
            case BodyPart::S_CYLINDER_Y :
            case BodyPart::S_CYLINDER_Z : ++_shape_cylinders[creature_index];
                break;
            case BodyPart::S_SPHERE : ++_shape_spheres[creature_index];
                break;
            default:
                assert(0);
        }
    }

    void Experiment::countConstraintLimits(int creature_index, double swing_x, double twist_y, double swing_z) {
        if (swing_x < 0.25 * Constraint::CONE_MAX_SWING) {
            ++_swing_x_blocked[creature_index];
        } else if (swing_x > 0.75 * Constraint::CONE_MAX_SWING) {
            ++_swing_x_free[creature_index];
        } else {
            ++_swing_x_medium[creature_index];
        }

        if (twist_y < 0.25 * Constraint::CONE_MAX_TWIST) {
            ++_twist_y_blocked[creature_index];
        } else if (twist_y > 0.75 * Constraint::CONE_MAX_TWIST) {
            ++_twist_y_free[creature_index];
        } else {
            ++_twist_y_medium[creature_index];
        }

        if (swing_z < 0.25 * Constraint::CONE_MAX_SWING) {
            ++_swing_z_blocked[creature_index];
        } else if (swing_z > 0.75 * Constraint::CONE_MAX_SWING) {
            ++_swing_z_free[creature_index];
        } else {
            ++_swing_z_medium[creature_index];
        }
    }

    void Experiment::loadConstraintLimits(int creature_index, const Creature& creature) {
        for (int i = 0; i < creature.getNumberOfConstraints(); ++i) {
            _swing_x[creature_index][i] = creature.getConstraint(i).getConeSwingX();
            _twist_y[creature_index][i] = creature.getConstraint(i).getConeTwistY();
            _swing_z[creature_index][i] = creature.getConstraint(i).getConeSwingZ();
        }
    }

    //TO XML

    QDomElement Experiment::headerToXml(QDomDocument& xml) const {
        QDomElement exp = xml.createElement("Experiment");
        exp.setAttribute("description", QString(_description.c_str()));
        exp.setAttribute("iterations", _iterations);
        exp.setAttribute("current-iteration", _current_iteration);
        exp.setAttribute("body-parts", _body_parts);
        exp.setAttribute("hidden-layers", _hidden_layers);
        exp.setAttribute("max-mass", _max_mass);
        exp.setAttribute("min-mass", _min_mass);
        exp.setAttribute("max-size", _max_size);
        exp.setAttribute("min-size", _min_size);
        exp.setAttribute("neurons-per-layer", _neurons_per_layer);
        exp.setAttribute("morphologies", _morphologies);
        exp.setAttribute("population-size", _population_size);
        exp.setAttribute("pso-sharing", "ring(2)");
        exp.setAttribute("pso-inertia", "linear(75%)");
        exp.setAttribute("pso-clamping", "tanh");
        exp.setAttribute("time-in-seconds", _time_in_seconds);
        return exp;
    }

    void Experiment::statisticsToXml(QDomDocument& xml, QDomElement * parent) const {
        QDomElement exp = headerToXml(xml);
        QDomElement stats = xml.createElement("Statistics");
        stats.setAttribute("fitness-in-time-max", gsl_stats_max(_fitness_in_time, 1, _current_iteration));
        stats.setAttribute("fitness-in-time-min", gsl_stats_min(_fitness_in_time, 1, _current_iteration));
        stats.setAttribute("fitness-in-time-mean", gsl_stats_mean(_fitness_in_time, 1, _current_iteration));
        stats.setAttribute("fitness-in-time-range", gsl_stats_max(_fitness_in_time, 1, _current_iteration) -
                gsl_stats_min(_fitness_in_time, 1, _current_iteration));
        stats.setAttribute("fitness-in-time-deviation", gsl_stats_sd(_fitness_in_time, 1, _current_iteration));
        double* sorted_fitness = new double[_current_iteration];
        Math::copy(_current_iteration, _fitness_in_time, sorted_fitness);
        gsl_sort(sorted_fitness, 1, _current_iteration);
        stats.setAttribute("fitness-in-time-median", gsl_stats_median_from_sorted_data(sorted_fitness, 1, _current_iteration));
        delete [] sorted_fitness;

        double* sorted_data = new double[_population_size];
        //fitness evolved
        stats.setAttribute("creature-fitness-max", gsl_stats_max(_creature_fitness, 1, _population_size));
        stats.setAttribute("creature-fitness-min", gsl_stats_min(_creature_fitness, 1, _population_size));
        stats.setAttribute("creature-fitness-mean", gsl_stats_mean(_creature_fitness, 1, _population_size));
        stats.setAttribute("creature-fitness-range", gsl_stats_max(_creature_fitness, 1, _population_size) -
                gsl_stats_min(_creature_fitness, 1, _population_size));
        stats.setAttribute("creature-fitness-deviation", gsl_stats_sd(_creature_fitness, 1, _population_size));
        Math::copy(_population_size, _creature_fitness, sorted_data);
        gsl_sort(sorted_data, 1, _population_size);
        stats.setAttribute("creature-fitness-median", gsl_stats_median_from_sorted_data(sorted_data, 1, _population_size));

        stats.setAttribute("creature-mass-max", gsl_stats_max(_creature_mass, 1, _population_size));
        stats.setAttribute("creature-mass-min", gsl_stats_min(_creature_mass, 1, _population_size));
        stats.setAttribute("creature-mass-mean", gsl_stats_mean(_creature_mass, 1, _population_size));
        stats.setAttribute("creature-mass-range", gsl_stats_max(_creature_mass, 1, _population_size) -
                gsl_stats_min(_creature_mass, 1, _population_size));
        stats.setAttribute("creature-mass-deviation", gsl_stats_sd(_creature_mass, 1, _population_size));
        Math::copy(_population_size, _creature_mass, sorted_data);
        gsl_sort(sorted_data, 1, _population_size);
        stats.setAttribute("creature-mass-median", gsl_stats_median_from_sorted_data(sorted_data, 1, _population_size));

        stats.setAttribute("creature-volume-max", gsl_stats_max(_creature_volume, 1, _population_size));
        stats.setAttribute("creature-volume-min", gsl_stats_min(_creature_volume, 1, _population_size));
        stats.setAttribute("creature-volume-mean", gsl_stats_mean(_creature_volume, 1, _population_size));
        stats.setAttribute("creature-volume-range", gsl_stats_max(_creature_volume, 1, _population_size) -
                gsl_stats_min(_creature_volume, 1, _population_size));
        stats.setAttribute("creature-volume-deviation", gsl_stats_sd(_creature_volume, 1, _population_size));
        Math::copy(_population_size, _creature_volume, sorted_data);
        gsl_sort(sorted_data, 1, _population_size);
        stats.setAttribute("creature-volume-median", gsl_stats_median_from_sorted_data(sorted_data, 1, _population_size));

        stats.setAttribute("root-mass-max", gsl_stats_max(_root_mass, 1, _population_size));
        stats.setAttribute("root-mass-min", gsl_stats_min(_root_mass, 1, _population_size));
        stats.setAttribute("root-mass-mean", gsl_stats_mean(_root_mass, 1, _population_size));
        stats.setAttribute("root-mass-range", gsl_stats_max(_root_mass, 1, _population_size) -
                gsl_stats_min(_root_mass, 1, _population_size));
        stats.setAttribute("root-mass-deviation", gsl_stats_sd(_root_mass, 1, _population_size));
        Math::copy(_population_size, _root_mass, sorted_data);
        gsl_sort(sorted_data, 1, _population_size);
        stats.setAttribute("root-mass-median", gsl_stats_median_from_sorted_data(sorted_data, 1, _population_size));

        stats.setAttribute("root-volume-max", gsl_stats_max(_root_volume, 1, _population_size));
        stats.setAttribute("root-volume-min", gsl_stats_min(_root_volume, 1, _population_size));
        stats.setAttribute("root-volume-mean", gsl_stats_mean(_root_volume, 1, _population_size));
        stats.setAttribute("root-volume-range", gsl_stats_max(_root_volume, 1, _population_size) -
                gsl_stats_min(_root_volume, 1, _population_size));
        stats.setAttribute("root-volume-deviation", gsl_stats_sd(_root_volume, 1, _population_size));
        Math::copy(_population_size, _root_volume, sorted_data);
        gsl_sort(sorted_data, 1, _population_size);
        stats.setAttribute("root-volume-median", gsl_stats_median_from_sorted_data(sorted_data, 1, _population_size));

        stats.setAttribute("non-root-mass-mean-max", gsl_stats_max(_non_root_mass_mean, 1, _population_size));
        stats.setAttribute("non-root-mass-mean-min", gsl_stats_min(_non_root_mass_mean, 1, _population_size));
        stats.setAttribute("non-root-mass-mean-mean", gsl_stats_mean(_non_root_mass_mean, 1, _population_size));
        stats.setAttribute("non-root-mass-mean-range", gsl_stats_max(_non_root_mass_mean, 1, _population_size) -
                gsl_stats_min(_non_root_mass_mean, 1, _population_size));
        stats.setAttribute("non-root-mass-mean-deviation", gsl_stats_sd(_non_root_mass_mean, 1, _population_size));
        Math::copy(_population_size, _creature_fitness, sorted_data);
        gsl_sort(sorted_data, 1, _population_size);
        stats.setAttribute("non-root-mass-mean-median", gsl_stats_median_from_sorted_data(sorted_data, 1, _population_size));

        stats.setAttribute("non-root-volume-mean-max", gsl_stats_max(_non_root_volume_mean, 1, _population_size));
        stats.setAttribute("non-root-volume-mean-min", gsl_stats_min(_non_root_volume_mean, 1, _population_size));
        stats.setAttribute("non-root-volume-mean-mean", gsl_stats_mean(_non_root_volume_mean, 1, _population_size));
        stats.setAttribute("non-root-volume-mean-range", gsl_stats_max(_non_root_volume_mean, 1, _population_size) -
                gsl_stats_min(_non_root_volume_mean, 1, _population_size));
        stats.setAttribute("non-root-volume-mean-deviation", gsl_stats_sd(_non_root_volume_mean, 1, _population_size));
        Math::copy(_population_size, _creature_fitness, sorted_data);
        gsl_sort(sorted_data, 1, _population_size);
        stats.setAttribute("non-root-volume-mean-median", gsl_stats_median_from_sorted_data(sorted_data, 1, _population_size));

        delete [] sorted_data;

        int capsules = 0, cylinders = 0, cones = 0, spheres = 0, parallelepipeds = 0;
        for (int i = 0; i < _population_size; ++i) {
            capsules += _shape_capsules[i];
            cylinders += _shape_cylinders[i];
            cones += _shape_cones[i];
            spheres += _shape_spheres[i];
            parallelepipeds += _shape_parallelepipeds[i];
        }

        int total_shapes = capsules + cylinders + cones + spheres + parallelepipeds;
        stats.setAttribute("shape-capsules", capsules * 100.0 / total_shapes);
        stats.setAttribute("shape-cones", cones * 100.0 / total_shapes);
        stats.setAttribute("shape-cylinders", cylinders * 100.0 / total_shapes);
        stats.setAttribute("shape-parallelepipeds", parallelepipeds * 100.0 / total_shapes);
        stats.setAttribute("shape-spheres", spheres * 100.0 / total_shapes);

        stats.setAttribute("shape-capsules-count", capsules);
        stats.setAttribute("shape-cones-count", cones);
        stats.setAttribute("shape-cylinders-count", cylinders);
        stats.setAttribute("shape-parallelepipeds-count", parallelepipeds);
        stats.setAttribute("shape-spheres-count", spheres);

        int root_capsules = 0, root_cylinders = 0, root_cones = 0, root_spheres = 0, root_parallelepipeds = 0;
        for (int i = 0; i < _population_size; ++i) {
            switch (_root_shape[i]) {
                case BodyPart::S_BOX : ++root_parallelepipeds;
                    break;
                case BodyPart::S_CAPSULE : ++root_capsules;
                    break;
                case BodyPart::S_CONE : ++root_cones;
                    break;
                case BodyPart::S_CYLINDER : ++root_cylinders;
                    break;
                case BodyPart::S_SPHERE : ++root_spheres;
                    break;
                default:
                    BDEBUG(TO_STRING(_root_shape[i]));
                    assert(0);
            }
        }
        int root_total = root_capsules + root_cylinders + root_cones + root_spheres + root_parallelepipeds;
        stats.setAttribute("root-shape-capsules", root_capsules * 100.0 / root_total);
        stats.setAttribute("root-shape-cones", root_cones * 100.0 / root_total);
        stats.setAttribute("root-shape-cylinders", root_cylinders * 100.0 / root_total);
        stats.setAttribute("root-shape-parallelepipeds", root_parallelepipeds * 100.0 / root_total);
        stats.setAttribute("root-shape-spheres", root_spheres * 100.0 / root_total);

        stats.setAttribute("root-shape-capsules-count", root_capsules);
        stats.setAttribute("root-shape-cones-count", root_cones);
        stats.setAttribute("root-shape-cylinders-count", root_cylinders);
        stats.setAttribute("root-shape-parallelepipeds-count", root_parallelepipeds);
        stats.setAttribute("root-shape-spheres-count", root_spheres);

        int non_root_capsules = capsules - root_capsules;
        int non_root_cones = cones - root_cones;
        int non_root_cylinders = cylinders - root_cylinders;
        int non_root_parallelepipeds = parallelepipeds - root_parallelepipeds;
        int non_root_spheres = spheres - root_spheres;
        int non_root_total = total_shapes - root_total;
        stats.setAttribute("non-root-shape-capsules", non_root_capsules * 100.0 / non_root_total);
        stats.setAttribute("non-root-shape-cones", non_root_cones * 100.0 / non_root_total);
        stats.setAttribute("non-root-shape-cylinders", non_root_cylinders * 100.0 / non_root_total);
        stats.setAttribute("non-root-shape-parallelepipeds", non_root_parallelepipeds * 100.0 / non_root_total);
        stats.setAttribute("non-root-shape-spheres", non_root_spheres * 100.0 / non_root_total);

        stats.setAttribute("non-root-shape-capsules-count", non_root_capsules);
        stats.setAttribute("non-root-shape-cones-count", non_root_cones);
        stats.setAttribute("non-root-shape-cylinders-count", non_root_cylinders);
        stats.setAttribute("non-root-shape-parallelepipeds-count", non_root_parallelepipeds);
        stats.setAttribute("non-root-shape-spheres-count", non_root_spheres);

        int swing_x_free = 0, swing_x_medium = 0, swing_x_blocked = 0;
        int twist_y_free = 0, twist_y_medium = 0, twist_y_blocked = 0;
        int swing_z_free = 0, swing_z_medium = 0, swing_z_blocked = 0;
        for (int i = 0; i < _population_size; ++i) {
            swing_x_free += _swing_x_free[i];
            swing_x_medium += _swing_x_medium[i];
            swing_x_blocked += _swing_x_blocked[i];
            twist_y_free += _twist_y_free[i];
            twist_y_medium += _twist_y_medium[i];
            twist_y_blocked += _twist_y_blocked[i];
            swing_z_free += _swing_z_free[i];
            swing_z_medium += _swing_z_medium[i];
            swing_z_blocked += _swing_z_blocked[i];
        }
        int swing_x = swing_x_free + swing_x_medium + swing_x_blocked;
        int twist_y = twist_y_free + twist_y_medium + twist_y_blocked;
        int swing_z = swing_z_free + swing_z_medium + swing_z_blocked;

        stats.setAttribute("swing-x-blocked", swing_x_blocked * 100.0 / swing_x);
        stats.setAttribute("swing-x-medium", swing_x_medium * 100.0 / swing_x);
        stats.setAttribute("swing-x-free", swing_x_free * 100.0 / swing_x);

        stats.setAttribute("twist-y-blocked", twist_y_blocked * 100.0 / twist_y);
        stats.setAttribute("twist-y-medium", twist_y_medium * 100.0 / twist_y);
        stats.setAttribute("twist-y-free", twist_y_free * 100.0 / twist_y);

        stats.setAttribute("swing-z-blocked", swing_z_blocked * 100.0 / swing_z);
        stats.setAttribute("swing-z-medium", swing_z_medium * 100.0 / swing_z);
        stats.setAttribute("swing-z-free", swing_z_free * 100.0 / swing_z);
        //Count
        stats.setAttribute("swing-x-blocked-count", swing_x_blocked);
        stats.setAttribute("swing-x-medium-count", swing_x_medium);
        stats.setAttribute("swing-x-free-count", swing_x_free);

        stats.setAttribute("twist-y-blocked-count", twist_y_blocked);
        stats.setAttribute("twist-y-medium-count", twist_y_medium);
        stats.setAttribute("twist-y-free-count", twist_y_free);

        stats.setAttribute("swing-z-blocked-count", swing_z_blocked);
        stats.setAttribute("swing-z-medium-count", swing_z_medium);
        stats.setAttribute("swing-z-free-count", swing_z_free);

        exp.appendChild(stats);

        QDomElement population = xml.createElement("Population");
        for (int i = 0; i < _population_size; ++i) {
            QDomElement creature = xml.createElement("Creature");
            creature.setAttribute("id", i);
            creature.setAttribute("fitness", _creature_fitness [i]);
            creature.setAttribute("shape-capsules-count", _shape_capsules [i]);
            creature.setAttribute("shape-cones-count", _shape_cones [i]);
            creature.setAttribute("shape-cylinders-count", _shape_cylinders [i]);
            creature.setAttribute("shape-parallelepipeds-count", _shape_parallelepipeds [i]);
            creature.setAttribute("shape-spheres-count", _shape_spheres [i]);
            total_shapes = _shape_capsules[i] + _shape_cones[i] +
                    _shape_cylinders[i] + _shape_parallelepipeds[i] + _shape_spheres[i];
            creature.setAttribute("shape-capsules", _shape_capsules [i] * 100.0 / total_shapes);
            creature.setAttribute("shape-cones", _shape_cones [i] * 100.0 / total_shapes);
            creature.setAttribute("shape-cylinders", _shape_cylinders [i] * 100.0 / total_shapes);
            creature.setAttribute("shape-parallelepipeds", _shape_parallelepipeds [i] * 100.0 / total_shapes);
            creature.setAttribute("shape-spheres", _shape_spheres [i] * 100.0 / total_shapes);

            creature.setAttribute("creature-mass", _creature_mass [i]);
            creature.setAttribute("root-shape", _root_shape[i]);
            creature.setAttribute("root-mass", _root_mass [i]);
            creature.setAttribute("non-root-mass-mean", _non_root_mass_mean [i]);
            creature.setAttribute("creature-volume", _creature_volume [i]);
            creature.setAttribute("root-volume", _root_volume [i]);
            creature.setAttribute("non-root-volume-mean", _non_root_volume_mean [i]);
            creature.setAttribute("morphology-index", _morphology_index[i]);

            creature.setAttribute("swing-x-blocked-count", _swing_x_blocked [i]);
            creature.setAttribute("swing-x-medium-count", _swing_x_medium [i]);
            creature.setAttribute("swing-x-free-count", _swing_x_free [i]);
            creature.setAttribute("twist-y-blocked-count", _twist_y_blocked [i]);
            creature.setAttribute("twist-y-medium-count", _twist_y_medium [i]);
            creature.setAttribute("twist-y-free-count", _twist_y_free [i]);
            creature.setAttribute("swing-z-blocked-count", _swing_z_blocked [i]);
            creature.setAttribute("swing-z-medium-count", _swing_z_medium [i]);
            creature.setAttribute("swing-z-free-count", _swing_z_free [i]);

            swing_x = _swing_x_free[i] + _swing_x_medium[i] + _swing_x_blocked[i];
            twist_y = _twist_y_free[i] + _twist_y_medium [i] + _twist_y_blocked[i];
            swing_z = _swing_z_free[i] + _swing_z_medium [i] + _swing_z_blocked[i];
            creature.setAttribute("swing-x-blocked", _swing_x_blocked [i] * 100.0 / swing_x);
            creature.setAttribute("swing-x-medium", _swing_x_medium [i]* 100.0 / swing_x);
            creature.setAttribute("swing-x-free", _swing_x_free [i]* 100.0 / swing_x);
            creature.setAttribute("twist-y-blocked", _twist_y_blocked [i]* 100.0 / twist_y);
            creature.setAttribute("twist-y-medium", _twist_y_medium [i]* 100.0 / twist_y);
            creature.setAttribute("twist-y-free", _twist_y_free [i]* 100.0 / twist_y);
            creature.setAttribute("swing-z-blocked", _swing_z_blocked [i]* 100.0 / swing_z);
            creature.setAttribute("swing-z-medium", _swing_z_medium [i]* 100.0 / swing_z);
            creature.setAttribute("swing-z-free", _swing_z_free [i]* 100.0 / swing_z);
            for (int j = 0; j < _body_parts - 1; ++j) {
                creature.setAttribute(QString(("constraint-swing-x-" + TO_STRING(j)).c_str()), _swing_x[i][j]);
                creature.setAttribute(QString(("constraint-twist-y-" + TO_STRING(j)).c_str()), _twist_y[i][j]);
                creature.setAttribute(QString(("constraint-swing-z-" + TO_STRING(j)).c_str()), _swing_z[i][j]);
            }
            population.appendChild(creature);
        }
        exp.appendChild(population);

        QDomElement fitness_in_time = xml.createElement("FitnessInTime");
        for (int i = 0; i < _current_iteration; ++i) {
            QDomElement iteration = xml.createElement("Iteration");
            iteration.setAttribute("iteration", i);
            iteration.setAttribute("fitness", _fitness_in_time[i]);
            fitness_in_time.appendChild(iteration);
        }
        exp.appendChild(fitness_in_time);

        if (_best_creature_morphology) {
            QDomElement best_creature_morphology = xml.createElement("BestCreatureMorphology");
            _best_creature_morphology->toXml(xml, &best_creature_morphology);
            exp.appendChild(best_creature_morphology);
        }
        if (_preferred_morphology) {
            QDomElement preferred_morphology = xml.createElement("PreferredMorphology");
            _preferred_morphology->toXml(xml, &preferred_morphology);
            exp.appendChild(preferred_morphology);
        }


        if (parent) {
            parent->appendChild(exp);
        } else {
            xml.appendChild(exp);
        }
    }

    void Experiment::bestCreaturesToXml(QDomDocument& xml, QDomElement * parent) const {
        QDomElement exp = headerToXml(xml);
        QDomElement best_creatures = xml.createElement("BestCreatures");
        int best_creatures_size = _current_iteration / _record_best_creatures_modulo;
        for (int i = 0; i < best_creatures_size; ++i) {
            _best_creatures[i]->toXml(xml, &best_creatures);
        }
        exp.appendChild(best_creatures);
        if (parent) {
            parent->appendChild(exp);
        } else {
            xml.appendChild(exp);
        }
    }

    void Experiment::evolvedCreaturesToXml(QDomDocument& xml, QDomElement * parent) const {
        QDomElement exp = headerToXml(xml);
        QDomElement population = xml.createElement("EvolvedCreatures");
        for (int i = 0; i < _population_size; ++i) {
            _evolved_creatures[i]->toXml(xml, &population);
        }
        exp.appendChild(population);
        if (parent) {
            parent->appendChild(exp);
        } else {
            xml.appendChild(exp);
        }
    }

    void Experiment::particlesToXml(QDomDocument& xml, QDomElement* parent) const {
        QDomElement exp = headerToXml(xml);
        QDomElement pso = xml.createElement("PSO");
        for (int i = 0; i < _population_size; ++i) {
            _pso->getParticle(i).toXml(xml,&pso);
        }
        exp.appendChild(pso);
        if (parent) {
            parent->appendChild(exp);
        } else {
            xml.appendChild(exp);
        }
    }

    int Experiment::main(int argc, char** argv) {


    }
}
